const SimpleTokenReader = require('./SimpleTokenReader')

// 有限状态机的各种状态
const DfaState = {
  Initial: 'initial',
  If: 'if',
  Id_if1: 'id_if1',
  Id_if2: 'id_if2',
  Else: 'else',
  Id_else1: 'id_else1',
  Id_else2: 'id_else2',
  Id_else3: 'id_else3',
  Id_else4: 'id_else4',
  Int: 'int',
  Id_int1: 'id_int1',
  Id_int2: 'id_int2',
  Id_int3: 'id_int3',
  Id: 'id',
  GT: 'gt',
  GE: 'ge',
  Assignment: 'assignment',
  Plus: 'plus',
  Minus: 'minus',
  Star: 'star',
  Slash: 'slash',
  SemiColon: 'semiColon',
  LeftParen: 'leftParen',
  RightParen: 'rightParen',
  IntLiteral: 'intLiteral'
}

/**
 * Token的类型
 */
 const TokenType = {
  Plus: 'Plus',   // +
  Minus: 'Minus',  // -
  Star: 'Star',   // *
  Slash: 'Slash',  // /

  GE: 'GE',     // >=
  GT: 'GT',     // >
  EQ: 'EQ',     // ==
  LE: 'LE',     // <=
  LT: 'LT',     // <

  SemiColon: 'SemiColon', // ;
  LeftParen: 'LeftParen', // (
  RightParen: 'RightParen',// )

  Assignment: 'Assignment',// =

  If: 'If',
  Else: 'Else',
  
  Int: 'Int',

  Identifier: 'Identifier',     //标识符

  IntLiteral: 'IntLiteral',     //整型字面量
  StringLiteral: 'StringLiteral'   //字符串字面量
}

// 是否是字母
const isAlpha = (ch) => {
  return /[a-zA-Z]/.test(ch)
}

// 是否是数字
const isDigit = (ch) => {
  return /[0-9]/.test(ch)
}

// 是否是空白字符
const isBlank = (ch) => {
  return ch === ' ' || ch === '\t' || ch === '\n'
}

const tokenPrint = (tokens) => {
  tokens.forEach(item => {
    console.log(`${item.type} ---- ${item.text}\n`)
  })
}

class SimpleLexer {
  tokenText // 临时保存token的文本
  tokens // 保存解析出来的Token
  token // 当前正在解析的Token

  constructor() {
    this.tokenText = ''
    this.tokens = []
  }

  // 把字符串转变成分词对象
  tokenize(code) {
    // 初始化
    this.tokens = []
    this.tokenText = ''
    this.token = {}
    let index = 0
    let state = DfaState.Initial

    while (index < code.length) {
      const ch = code[index]

      switch (state) {
        case 'initial':
          state = this.initToken(ch)
          break
        case 'id':
          if (isAlpha(ch) || isDigit(ch)) {
            this.tokenText = this.tokenText.concat(ch)
          } else {
            state = this.initToken(ch)
          }
          break
        case 'gt':
          if (ch === '=') { // 大于等于的情况
            this.token.type = TokenType.GE
            state = DfaState.GE
            this.tokenText = this.tokenText.concat(ch)
          } else {
            state = this.initToken(ch)
          }
          break
          case 'ge':
          case 'assignment':
          case 'plus':
          case 'minus':
          case 'star':
          case 'slash':
          case 'semiColon':
          case 'leftParen':
          case 'rightParen':
              state = this.initToken(ch);          //退出当前状态，并保存Token
              break;
          case 'intLiteral':
            if (isDigit(ch)) {
              this.tokenText = this.tokenText.concat(ch)
            } else {
              state = this.initToken(ch)
            }
            break
          case 'id_int1': // int 在i时的情况
            if (ch === 'n') {
              state = DfaState.Id_int2
              this.tokenText = this.tokenText.concat(ch)
            } else if (isDigit(ch) || isAlpha(ch)) {
              state = DfaState.Id
              this.tokenText = this.tokenText(concat(ch))
            } else {
              state = this.initToken(ch)
            }
            break
          case 'id_int2':
            if (ch === 't') {
              state = DfaState.Id_int3
              this.tokenText = this.tokenText.concat(ch)
            } else if (isDigit(ch) || isAlpha(ch)) {
              state = DfaState.Id // 切换回id状态
              this.tokenText = this.tokenText.concat(ch)
            } else {
              state = this.initToken(ch)
            }
            break
          case 'id_int3':
            if (isBlank(ch)) {
              this.token.type = TokenType.Int
              state = this.initToken(ch)
            } else {
              state = DfaState.Id
              this.tokenText = this.tokenText.concat(ch)
            }
            break
          default:
      }
      index++

    }

    // 把最后一个token送进去
    if (this.tokenText.length > 0) {
      this.initToken()
    }
    return this.tokens
  }

  /**
   * 有限状态机进入初始状态。
   * 这个初始状态其实并不做停留，它马上进入其他状态。
   * 开始解析的时候，进入初始状态；某个Token解析完毕，也进入初始状态，在这里把Token记下来，然后建立一个新的Token。
   * @param ch
   * @return
   */
  initToken(ch) {
    if (this.tokenText.length > 0) {
      this.token.text = this.tokenText
      this.tokens.push(this.token)

      this.tokenText = ''
      this.token = {}
    }

    let newState = DfaState.Initial
    if (isAlpha(ch)) {  //第一个字符是字母
      if (ch === 'i') {  // int类型的可能
        newState = DfaState.Id_int1
      } else {
        newState = DfaState.Id
      }
      this.token.type = TokenType.Identifier
      this.tokenText = this.tokenText.concat(ch) // 追加字符
    } else if (isDigit(ch)) {  // //第一个字符是数字
      newState = DfaState.IntLiteral
      this.token.type = TokenType.IntLiteral
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === '>') {
      newState = DfaState.GT
      this.token.type = TokenType.GT
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === '+') {
      newState = DfaState.Plus
      this.token.type = TokenType.Plus
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === '-') {
      newState = DfaState.Minus
      this.token.type = TokenType.Minus
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === '*') {
      newState = DfaState.Star
      this.token.type = TokenType.Star
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === '／') {
      newState = DfaState.Slash
      this.token.type = TokenType.Slash
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === ';') {
      newState = DfaState.SemiColon
      this.token.type = TokenType.SemiColon
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === '(') {
      newState = DfaState.LeftParen
      this.token.type = TokenType.LeftParen
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === ')') {
      newState = DfaState.RightParen
      this.token.type = TokenType.RightParen
      this.tokenText = this.tokenText.concat(ch)
    } else if (ch === '=') {
      newState = DfaState.Assignment
      this.token.type = TokenType.Assignment
      this.tokenText = this.tokenText.concat(ch)
    } else {
      newState = DfaState.Initial
    }
    return newState
  }

  /**
   * 
   * @param code 代码 
   * @returns {SimpleTokenReader}
   */
  genSimpleTokenReader(code) {
    if (code) {
      const tokens = this.tokenize(code)
      return new SimpleTokenReader(tokens)
    } else {
      return new SimpleTokenReader(this.tokens)
    }
  }
}

module.exports = {
  SimpleLexer,
  tokenPrint
}